package cn.mw.cmdb.timetask.service.impl;

import cn.mw.cmdb.exception.TimeTaskException;
import cn.mw.cmdb.exception.WarnTitleException;
import cn.mw.cmdb.mongoMapper.MwTimeTaskTypeMapper;
import cn.mw.cmdb.service.impl.BaseSearchService;
import cn.mw.cmdb.timeplan.entity.MwTimePlan;
import cn.mw.cmdb.timetask.entity.MwTimeTaskDto;
import cn.mw.cmdb.timetask.entity.MwTimeTaskTypeDto;
import cn.mw.cmdb.timetask.entity.TaskRecordDto;
import cn.mw.cmdb.timetask.param.MwTimeTaskParam;
import cn.mw.cmdb.timetask.service.MwTimeTaskService;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.microMonitorCommon.api.common.ResponseBase;
import cn.mw.microMonitorCommon.api.powerjob.PowerJobRequestParam;
import cn.mw.microMonitorCommon.api.powerjob.PowerJobService;
import cn.mw.microMonitorCommon.entity.common.PowerJobAppInfoDTO;
import cn.mw.microMonitorCommon.entity.powerjob.TaskInstanceInfo;
import cn.mw.microMonitorCommon.param.MwJobParam;
import cn.mw.microMonitorCommon.util.DateUtils;
import cn.mw.microMonitorCommon.util.IDModelType;
import cn.mw.microMonitorCommon.util.ModuleIDManager;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import tech.powerjob.common.enums.ExecuteType;
import tech.powerjob.common.enums.ProcessorType;
import tech.powerjob.common.enums.TimeExpressionType;

import java.util.*;

/**
 * @description 定时任务
 */
@Slf4j
@Service
public class MwTimeTaskServiceImpl extends BaseSearchService<MwTimeTaskTypeDto> implements MwTimeTaskService {

    private MwTimeTaskTypeMapper timeTaskTypeMapper;

    private ModuleIDManager moduleIDManager;

    @DubboReference(check = false,timeout = 12000)
    private PowerJobService powerJobService;

    public MwTimeTaskServiceImpl(MwTimeTaskTypeMapper timeTaskTypeMapper,ModuleIDManager moduleIDManager) {
        this.timeTaskTypeMapper = timeTaskTypeMapper;
        this.moduleIDManager = moduleIDManager;
    }

    @Override
    public void saveTimeTaskType(MwTimeTaskTypeDto timeTaskTypeDto) {
        //名称校验
        Query query = new Query();
        query.addCriteria(new Criteria(CMDBTool.getFieldName(MwTimeTaskTypeDto::getTaskModule)).is(timeTaskTypeDto.getTaskModule()));
        if(StringUtils.isNotEmpty(timeTaskTypeDto.getId())){
            query.addCriteria(new Criteria(CMDBTool.getFieldName(MwTimePlan::getId)).ne(timeTaskTypeDto.getId()));
        }
        long count = timeTaskTypeMapper.count(query);
        if(count > 0){
            throw new WarnTitleException("time-plan-name-check");
        }
        if(StringUtils.isEmpty(timeTaskTypeDto.getId())){
            timeTaskTypeDto.setId(moduleIDManager.getIDStr(IDModelType.MyMonitor));
        }
        timeTaskTypeMapper.save(timeTaskTypeDto);
    }

    @Override
    public void deleteTimeTaskType(MwTimeTaskParam timeTaskParam) {
        if(CollectionUtils.isEmpty(timeTaskParam.getTimeTaskTypeIds())){
            return;
        }
        //获取分类下有没有任务，如果有，不允许删除
        Query query = new Query();
        query.addCriteria(new Criteria(CMDBTool.getFieldName(MwTimeTaskTypeDto::getId)).in(timeTaskParam.getTimeTaskTypeIds()));
        List<MwTimeTaskTypeDto> timeTaskTypeDtoList = timeTaskTypeMapper.find(query);
        if(CollectionUtils.isNotEmpty(timeTaskTypeDtoList)){
            for (MwTimeTaskTypeDto timeTaskTypeDto : timeTaskTypeDtoList) {
                List<Long> timeTaskIds = timeTaskTypeDto.getTimeTaskIds();
                if(CollectionUtils.isNotEmpty(timeTaskIds)){
                    throw new WarnTitleException("time-task-relation-delete-check");
                }
            }
        }
        timeTaskTypeMapper.remove(query);
    }

    @Override
    public List<MwTimeTaskTypeDto> selectTimeTaskTypeList(MwTimeTaskParam timeTaskParam) {
        Query query = new Query();
        if(timeTaskParam.getTimeTaskType() != null){
            query.addCriteria(new Criteria(CMDBTool.getFieldName(MwTimeTaskTypeDto::getTaskType)).is(timeTaskParam.getTimeTaskType()));
        }
        List<MwTimeTaskTypeDto> timeTaskTypeDtos = timeTaskTypeMapper.find(query);
        return timeTaskTypeDtos;
    }

    @Override
    public Page selectTimeTask(MwTimeTaskParam timeTaskParam) {
        if(timeTaskParam.getTimeTaskIds() != null && timeTaskParam.getTimeTaskIds().size() == 0){
            return new Page();
        }
        List<MwTimeTaskDto> timeTaskDtoList = new ArrayList<>();
        MwJobParam jobParam = new MwJobParam();
        jobParam.setJobIds(timeTaskParam.getTimeTaskIds());
        jobParam.setJobName(timeTaskParam.getJobName());
        jobParam.setPageNum(timeTaskParam.getPageNum());
        jobParam.setPageSize(timeTaskParam.getPageSize());
        List<PowerJobAppInfoDTO> dtos = powerJobService.selectJobTask(jobParam);
        if(CollectionUtils.isEmpty(dtos)){
            return new Page();
        }
        for (PowerJobAppInfoDTO dto : dtos) {
            MwTimeTaskDto timeTaskDto = new MwTimeTaskDto();
            BeanUtils.copyProperties(dto, timeTaskDto);
            timeTaskDtoList.add(timeTaskDto);
        }
        Page page = pageHandle(timeTaskDtoList, timeTaskParam.getPageNum(), timeTaskParam.getPageSize());
        List<MwTimeTaskDto> records = page.getRecords();
        timeTaskMatchGroup(records);
        return page;
    }

    @Override
    public void save(MwTimeTaskDto timeTaskDto) {
        PowerJobRequestParam jobRequestParam = dataConvert(timeTaskDto);
        ResponseBase<Long> responseBase = powerJobService.saveOrUpdateJob(jobRequestParam);
        Long taskId = responseBase.getData();
        if(taskId == null || responseBase.getRtnCode() == 500){
            throw new TimeTaskException(responseBase.getMsg());
        }
        //查询分组
        if(StringUtils.isEmpty(timeTaskDto.getTaskGroupId())){
            return;
        }
        Query query = new Query();
        query.addCriteria(new Criteria(CMDBTool.getFieldName(MwTimeTaskTypeDto::getId)).is(timeTaskDto.getTaskGroupId()));
        MwTimeTaskTypeDto timeTaskTypeDto = timeTaskTypeMapper.findById(timeTaskDto.getTaskGroupId());
        List<Long> timeTaskIds = timeTaskTypeDto.getTimeTaskIds();
        if(timeTaskIds == null){
            timeTaskIds = new ArrayList<>();
        }
        if(timeTaskIds.contains(taskId)){
            return;
        }
        timeTaskIds.add(taskId);
        timeTaskTypeDto.setTimeTaskIds(timeTaskIds);
        saveTimeTaskType(timeTaskTypeDto);
    }

    @Override
    public void remove(MwTimeTaskParam timeTaskParam) throws Exception {
        if(CollectionUtils.isEmpty(timeTaskParam.getTimeTaskIds())){
            return;
        }
        List<Long> timeTaskIds = timeTaskParam.getTimeTaskIds();
        powerJobService.deleteJob(timeTaskIds);
        Query query = new Query();
        query.addCriteria(CriteriaWrapper.where(MwTimeTaskTypeDto::getTimeTaskIds).elemMatch(new Criteria().in(timeTaskIds)));
        //删除分组下的数据
        List<MwTimeTaskTypeDto> timeTaskTypeDtos = timeTaskTypeMapper.find(query);
        if(CollectionUtils.isEmpty(timeTaskTypeDtos)){
            return;
        }
        List<Pair<Query, Update>> updateList = new ArrayList<>();
        BulkOperations operations = timeTaskTypeMapper.bulkOps(BulkOperations.BulkMode.UNORDERED, MwTimeTaskTypeDto.class);
        for (MwTimeTaskTypeDto timeTaskTypeDto : timeTaskTypeDtos) {
            List<Long> taskIds = timeTaskTypeDto.getTimeTaskIds();
            if(CollectionUtils.isEmpty(taskIds)){
                continue;
            }
            Iterator<Long> iterator = taskIds.iterator();
            while (iterator.hasNext()){
                Long taskId = iterator.next();
                if(timeTaskIds.contains(taskId)){
                    iterator.remove();
                }
            }
            timeTaskTypeDto.setTimeTaskIds(taskIds);
            Query updateQuery = new Query(CriteriaWrapper.where(MwTimeTaskTypeDto::getId).is(timeTaskTypeDto.getId()));
            Update update = new Update();
            // 更新数据
            update.set(CMDBTool.getFieldName(MwTimeTaskTypeDto::getTimeTaskIds), taskIds);
            Pair<Query, Update> updatePair = Pair.of(updateQuery, update);
            updateList.add(updatePair);
        }
        if(CollectionUtils.isNotEmpty(updateList)){
            operations.updateMulti(updateList);
            operations.execute();
        }
    }

    @Override
    public Page getTaskExecuteRecord(MwTimeTaskParam timeTaskParam) {
        MwJobParam jobParam = new MwJobParam();
        jobParam.setJobIds(timeTaskParam.getTimeTaskIds());
        jobParam.setTaskStatus(timeTaskParam.getTaskStatus());
        List<TaskInstanceInfo> taskInstanceInfo = powerJobService.getTaskInstanceInfo(jobParam);
        if(CollectionUtils.isEmpty(taskInstanceInfo)){
            return new Page();
        }
        //数据需要时间排序
        taskInstanceSort(taskInstanceInfo);
        List<TaskRecordDto> recordDtoList = new ArrayList<>();
        for (TaskInstanceInfo instanceInfo : taskInstanceInfo) {
            TaskRecordDto recordDto = new TaskRecordDto();
            BeanUtils.copyProperties(instanceInfo, recordDto);
            recordDtoList.add(recordDto);
        }
        Page page = pageHandle(recordDtoList, timeTaskParam.getPageNum(), timeTaskParam.getPageSize());
        return page;
    }


    private void taskInstanceSort( List<TaskInstanceInfo> instanceInfos){
        Collections.sort(instanceInfos, new Comparator<TaskInstanceInfo>() {
            @Override
            public int compare(TaskInstanceInfo o1, TaskInstanceInfo o2) {
                if(StringUtils.isEmpty(o1.getActualTriggerTime()) || StringUtils.isEmpty(o2.getActualTriggerTime())){
                    return 0;
                }
                try {
                    if(DateUtils.parse(o1.getActualTriggerTime()).compareTo(DateUtils.parse(o2.getActualTriggerTime())) < 0){
                        return 1;
                    }
                    if(DateUtils.parse(o1.getActualTriggerTime()).compareTo(DateUtils.parse(o2.getActualTriggerTime())) > 0){
                        return -1;
                    }
                    return 0;
                }catch (Throwable e){
                    log.info("taskInstanceSort() dateConvertError:"+o1.getActualTriggerTime()+"::::"+o2.getActualTriggerTime());
                    return 0;
                }
            }
        });
    }

    @Override
    public TaskRecordDto getTaskInstanceDetails(MwTimeTaskParam timeTaskParam) {
        if(StringUtils.isEmpty(timeTaskParam.getTaskInstanceId())){
            return new TaskRecordDto();
        }
        MwJobParam jobParam = new MwJobParam();
        jobParam.setTaskInstanceId(Long.parseLong(timeTaskParam.getTaskInstanceId()));
        TaskInstanceInfo instanceInfo = powerJobService.getTaskInstanceDetail(jobParam);
        TaskRecordDto recordDto = new TaskRecordDto();
        BeanUtils.copyProperties(instanceInfo, recordDto);
        return recordDto;
    }

    @Override
    public TaskRecordDto getTaskRunLog(MwTimeTaskParam timeTaskParam) {
        if(StringUtils.isEmpty(timeTaskParam.getTaskInstanceId())){
            return new TaskRecordDto();
        }
        MwJobParam jobParam = new MwJobParam();
        jobParam.setTaskInstanceId(Long.parseLong(timeTaskParam.getTaskInstanceId()));
        String taskInstanceLog = powerJobService.getTaskInstanceLog(jobParam);
        TaskRecordDto recordDto = new TaskRecordDto();
        recordDto.setTaskRunLog(taskInstanceLog);
        return recordDto;
    }

    @Override
    public void runTask(MwTimeTaskParam timeTaskParam) {
        MwJobParam jobParam = new MwJobParam();
        jobParam.setJobIds(timeTaskParam.getTimeTaskIds());
        jobParam.setInstanceParams(timeTaskParam.getInstanceParams());
        ResponseBase<Long> responseBase = powerJobService.runTask(jobParam);
    }

    @Override
    public void updateTaskStatus(MwTimeTaskParam timeTaskParam) {
        MwJobParam jobParam = new MwJobParam();
        jobParam.setJobIds(timeTaskParam.getTimeTaskIds());
        jobParam.setEnableTask(timeTaskParam.getStatus());
        powerJobService.taskStatusUpdate(jobParam);
    }

    @Override
    public void taskRetry(MwTimeTaskParam timeTaskParam) {
        if(StringUtils.isEmpty(timeTaskParam.getTaskInstanceId())){
            return;
        }
        MwJobParam jobParam = new MwJobParam();
        jobParam.setTaskInstanceId(Long.parseLong(timeTaskParam.getTaskInstanceId()));
        powerJobService.taskRetry(jobParam);
    }

    @Override
    public void taskStop(MwTimeTaskParam timeTaskParam) {
        try {
            if(StringUtils.isEmpty(timeTaskParam.getTaskInstanceId())){
                return;
            }
            MwJobParam jobParam = new MwJobParam();
            jobParam.setTaskInstanceId(Long.parseLong(timeTaskParam.getTaskInstanceId()));
            powerJobService.taskStop(jobParam);
        }catch (Throwable e){
            log.error("MwTimeTaskServiceImpl{} taskStop error:",e);
        }

    }

    @Override
    public List checkTimeExpression(MwTimeTaskParam timeTaskParam) {
        MwJobParam jobParam = new MwJobParam();
        jobParam.setTimeExpression(timeTaskParam.getTimeExpression());
        jobParam.setTimeExpressionType(timeTaskParam.getTimeExpressionType());
        List<String> checkMessage = powerJobService.timeExpressionCheck(jobParam);
        return checkMessage;
    }


    private PowerJobRequestParam dataConvert(MwTimeTaskDto timeTaskDto){
        PowerJobRequestParam jobRequestParam = new PowerJobRequestParam();
        jobRequestParam.setId(timeTaskDto.getId());
        jobRequestParam.setJobName(timeTaskDto.getJobName());
        jobRequestParam.setJobParams(timeTaskDto.getJobParams());
        jobRequestParam.setJobDescription(timeTaskDto.getJobDescription());
        jobRequestParam.setTimeExpressionType(TimeExpressionType.of(timeTaskDto.getTimeExpressionType()).name());
        jobRequestParam.setTimeExpression(timeTaskDto.getTimeExpression());
        jobRequestParam.setExecuteType(ExecuteType.of(timeTaskDto.getExecuteType()).name());
        jobRequestParam.setProcessorType(ProcessorType.of(timeTaskDto.getProcessorType()).name());
        jobRequestParam.setProcessorInfo(timeTaskDto.getProcessorInfo());
        jobRequestParam.setDispatchStrategy(timeTaskDto.getDispatchStrategy());
        jobRequestParam.setInstanceTimeLimit(timeTaskDto.getInstanceTimeLimit());
        jobRequestParam.setMaxInstanceNum(timeTaskDto.getMaxInstanceNum());
        jobRequestParam.setConcurrency(timeTaskDto.getConcurrency());
        jobRequestParam.setLifeCycleStart(timeTaskDto.getLifeCycleStart());
        jobRequestParam.setLifeCycleEnd(timeTaskDto.getLifeCycleEnd());
        return jobRequestParam;
    }

    private Page pageHandle(List list, Integer pageNum, Integer pageSize){
        if(CollectionUtils.isNotEmpty(list)){
            int fromIndex = pageSize * (pageNum -1);
            int toIndex = pageSize * pageNum;
            if(toIndex > list.size()){
                toIndex = list.size();
            }
            List subList = list.subList(fromIndex, toIndex);
            Page page = new Page();
            page.setRecords(subList);
            page.setTotal(list.size());
            return page;
        }
        return new Page();
    }

    private void timeTaskMatchGroup(List<MwTimeTaskDto> timeTaskDtoList){
        //获取分组信息
        List<MwTimeTaskTypeDto> timeTaskTypeDtos = selectTimeTaskTypeList(new MwTimeTaskParam());
        if(CollectionUtils.isEmpty(timeTaskDtoList)){
            return;
        }
        for (MwTimeTaskTypeDto timeTaskTypeDto : timeTaskTypeDtos) {
            List<Long> timeTaskIds = timeTaskTypeDto.getTimeTaskIds();
            if(CollectionUtils.isEmpty(timeTaskIds)){
                continue;
            }
            for (MwTimeTaskDto timeTaskDto : timeTaskDtoList) {
                if(timeTaskIds.contains(timeTaskDto.getId())){
                    timeTaskDto.setTaskGroupId(timeTaskTypeDto.getId());
                }
            }
        }
    }

    @Override
    protected MwTimeTaskTypeDto genObject() {
        return new MwTimeTaskTypeDto();
    }
}
